AStarControl = {}
local self = AStarControl
Control:Register(self)

function self:Init()
    self.diff_lev = AStarConstant.DiffLev.I

    self.max_x = 0
    self.max_y = 0

    self.grids = {}
    self.open_list = {}
    self.close_list = {}
end

function self:MakeGrid(x, y)
    return {
        x = x,
        y = y,
        f = 0,
        g = 0,
        h = 0,
        parent = nil,
        grid_type = AStarConstant.GripType.Node
    }
end

function self:SetGridType(x, y, type)
    local grid = self.grids[y][x]
    if type == AStarConstant.GripType.Start then
        for ky, arr_y in pairs(self.grids) do
            for k, grid in pairs(arr_y) do
                grid.f = 0
                grid.g = 0
                grid.h = 0
                grid.parent = nil
                if grid.grid_type == AStarConstant.GripType.Start then
                    grid.grid_type = AStarConstant.GripType.Node
                end
            end
        end
    elseif type == AStarConstant.GripType.Wall then
        if grid.grid_type == AStarConstant.GripType.Start then
            return
        elseif grid.grid_type == AStarConstant.GripType.Wall then
            type =  AStarConstant.GripType.Node
        end
    end
    grid.grid_type = type or AStarConstant.GripType.Node
end

function self:ReSetGrid()
    for ky, arr_y in pairs(self.grids) do
        for k, grid in pairs(arr_y) do
            grid.f = 0
            grid.g = 0
            grid.h = 0
            grid.parent = nil
            grid.grid_type = AStarConstant.GripType.Node
        end
    end
    self.grids[AStarConstant.StartPos.Y][AStarConstant.StartPos.X].grid_type = AStarConstant.GripType.Start
end

function self:SetDiff(diff_lev)
    self.diff_lev = diff_lev or self.diff_lev
    local args = AStarConstant.DiffArgs[self.diff_lev]
    self.max_x = args.X
    self.max_y = args.Y
    local arr_y = nil
    for y = 1, args.Y do
        for x = 1, args.X do
            arr_y = self.grids[y]
            if arr_y == nil then
                arr_y = {}
                self.grids[y] = arr_y
            end
            arr_y[x] = self:MakeGrid(x, y)
        end
    end
    local grid = nil
    -- 还原参数
    for k, arr in pairs(self.grids) do
        for k, grid in pairs(arr) do
            grid.f = 0
            grid.g = 0
            grid.h = 0
            grid.grid_type = AStarConstant.GripType.Node
        end
    end
    --默认第一个起点
    self.grids[AStarConstant.StartPos.Y][AStarConstant.StartPos.X].grid_type = AStarConstant.GripType.Start
    EventListener:Broadcast(AStarEvent.ASTAR_DIFF_UP)
end

function self:GetGrid(x, y)
    return self.grids[y][x]
end

function self:Nav(fx, fy, tx, ty)
    --重置数据
    for k, v in pairs(self.open_list) do
        self.open_list[k] = nil
    end
    for k, v in pairs(self.close_list) do
        self.close_list[k] = nil
    end
    for ky, arr_y in pairs(self.grids) do
        for k, grid in pairs(arr_y) do
            grid.f = 0
            grid.g = 0
            grid.h = 0
            grid.parent = nil
            if grid.grid_type == AStarConstant.GripType.End then
                grid.grid_type = AStarConstant.GripType.Node
            end
        end
    end

    --标记中点
    local to_grid = self.grids[ty][tx]
    if to_grid.grid_type == AStarConstant.GripType.Wall then
        return {}
    end
    to_grid.grid_type = AStarConstant.GripType.End

    --起点
    local from_grid = self.grids[fy][fx]
    table.insert(self.open_list, from_grid)
    local grid = self.open_list[1]
    local paths = {}
    while #self.open_list > 0 and grid.grid_type ~= AStarConstant.GripType.End do
        grid = self.open_list[1]
        if grid.grid_type == AStarConstant.GripType.End then
            self:NavResult(paths, grid)
            Debug.Log("结束")
        else
            -- self:GHF(0 - 1, 0 + 1, grid)
            -- self:GHF(0 - 1, 0 - 1, grid)
            -- self:GHF(0 - 1, 0 - 1, grid)
            -- self:GHF(0 + 1, 0 + 1, grid)
            --上下
            self:GHF(0, 0 - 1, grid)
            self:GHF(0, 0 + 1, grid)
            --左右
            self:GHF(0 - 1, 0, grid)
            self:GHF(0 + 1, 0, grid)
        end
        table.insert(self.close_list, grid)
        TableUtil.Remove(self.open_list, grid)
        if (#self.open_list == 0) then
            Debug.Log("No find open list !")
        end
    end
    return paths
end

function self:NavResult(paths, grid)
    if grid and grid.parent then
        table.insert(paths, grid)
        self:NavResult(paths, grid.parent)
        grid.parent = nil
    end
end

function self:GHF(offs_x, offs_y, grid)
    local x = grid.x + offs_x
    local y = grid.y + offs_y
    if x < 0 or y < 0 then
        return
    end
    if x > self.max_x or y > self.max_y then
        return
    end
    if self.grids[y] == nil or self.grids[y][x] == nil then
        return
    end
    local temp_grid = self.grids[y][x]
    if temp_grid.grid_type == AStarConstant.GripType.Wall then
        return
    end
    if TableUtil.IsHas(self.close_list, temp_grid) then
        return
    end

    local g = grid.g + math.floor(math.sqrt((math.abs(offs_x) + math.abs(offs_y))) * 10)
    if temp_grid.g == 0 or temp_grid.g > g then
        temp_grid.g = g
        temp_grid.parent = grid
    end

    temp_grid.h = self:Manhattan(grid.x, grid.y, x, y)
    temp_grid.f = temp_grid.h + temp_grid.g

    if (not TableUtil.IsHas(self.open_list, temp_grid)) then
        table.insert(self.open_list, temp_grid)
    end
    table.sort(
        self.open_list,
        function(a, b)
            return a.f < b.f
        end
    )
end

--曼哈顿方式计算H值
function self:Manhattan(fx, fy, tx, ty)
    return math.floor((math.abs(tx - fx) + math.abs(ty - fy)) * 10)
end
